package com.fileupload.upload.log;

import com.fileupload.config.ApplicationConfig;
import com.fileupload.mapper.FileUploadLogMapper;
import com.fileupload.model.entity.FileUploadLog;
import com.fileupload.upload.log.strategy.LogStorageStrategy;
import com.fileupload.upload.log.strategy.LogStorageStrategyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 批量日志收集服务
 * 负责收集文件上传日志，并在满足条件时执行批量插入
 * @author 小乙
 */
@Service
public class BatchLogCollectorService {
    private static final Logger logger = LoggerFactory.getLogger(BatchLogCollectorService.class);

    @Autowired
    private BatchInsertConfig batchInsertConfig;
    
    @Autowired
    private ApplicationConfig applicationConfig;

    @Autowired
    private LogStorageStrategyFactory strategyFactory;

    // 使用线程安全的集合存储待插入的日志（仅用于MySQL策略的批处理）
    private final CopyOnWriteArrayList<FileUploadLog> pendingLogs = new CopyOnWriteArrayList<>();

    // 使用锁保证批量插入的原子性
    private final ReentrantLock lock = new ReentrantLock();

    /**
     * 添加日志到存储系统
     * 
     * @param log 日志对象
     */
    public void addLog(FileUploadLog log) {
        if (log == null) {
            logger.warn("尝试添加空日志对象，已忽略");
            return;
        }
        
        String fileName = log.getUploadFileName();
        String monitorName = log.getMonitorName();
        
        // 检查日志是否需要入库
        if (!applicationConfig.isLogDatabaseEnabled()) {
            logger.debug("[批量收集] 日志入库功能已关闭，仅记录日志不入库 - 文件名: {}, 监控组: {}", 
                    fileName, monitorName);
            return;
        }
        
        LogStorageStrategy currentStrategy = strategyFactory.getCurrentStrategy();
        logger.debug("[批量收集] 使用存储策略: {} - 文件名: {}, 监控组: {}", 
                currentStrategy.getType(), fileName, monitorName);
        
        // 对于MySQL策略，使用批处理机制
        if ("mysql".equals(currentStrategy.getType())) {
            handleMysqlStrategy(log, fileName, monitorName, currentStrategy);
        } else {
            // 对于其他策略（如Redis），直接存储
            try {
                boolean result = currentStrategy.storeLog(log);
                logger.info("[批量收集] {}策略添加日志{} - 文件名: {}, 监控组: {}",
                        currentStrategy.getType().toUpperCase(),
                        result ? "成功" : "失败",
                        fileName, monitorName);
            } catch (Exception e) {
                logger.error("[批量收集] {}策略添加日志异常 - 文件名: {}, 错误信息: {}",
                        currentStrategy.getType().toUpperCase(),
                        fileName, e.getMessage(), e);
            }
        }
    }
    
    /**
     * 处理MySQL策略的日志存储
     */
    private void handleMysqlStrategy(FileUploadLog log, String fileName, String monitorName, LogStorageStrategy strategy) {
        lock.lock();
        try {
            log.setInsertTime(System.currentTimeMillis());
            pendingLogs.add(log);
            logger.info("[批量收集] 添加日志到MySQL批处理队列 - 文件名: {}, 监控组: {}, 当前待插入数量: {}", 
                    fileName, monitorName, pendingLogs.size());
        
            // 检查是否达到阈值
            if (pendingLogs.size() >= batchInsertConfig.getRecordThreshold()) {
                logger.info("[批量收集] 待插入日志数量达到阈值({}/{}), 触发批量插入", 
                        pendingLogs.size(), batchInsertConfig.getRecordThreshold());
                // 解锁后再执行批量插入，避免长时间持有锁
                lock.unlock();
                try {
                    boolean result = executeBatchInsert();
                    logger.info("[批量收集] 阈值触发的批量插入执行结果: {}", result ? "成功" : "失败");
                } finally {
                    // 重新获取锁以便在方法结束时正确释放
                    lock.lock();
                }
            }
        } catch (Exception e) {
            logger.error("[批量收集] 添加日志到MySQL队列异常 - 文件名: {}, 错误信息: {}", 
                    fileName, e.getMessage(), e);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 执行批量插入操作
     * @return 是否执行成功
     */
    public boolean executeBatchInsert() {
        // 检查日志是否需要入库
        if (!applicationConfig.isLogDatabaseEnabled()) {
            logger.info("[批量插入] 日志入库功能已关闭，跳过批量插入操作");
            
            // 清空待处理列表，避免内存占用
            lock.lock();
            try {
                int count = pendingLogs.size();
                pendingLogs.clear();
                logger.info("[批量插入] 已清空待处理日志列表，共 {} 条记录", count);
            } finally {
                lock.unlock();
            }
            return true;
        }
        
        // 获取MySQL策略（批量插入仅用于MySQL策略）
        LogStorageStrategy mysqlStrategy = strategyFactory.getStrategyByType("mysql");
        
        List<FileUploadLog> logsToInsert = new ArrayList<>();
        int batchSize = 0;
        
        logger.info("[批量插入] 开始执行批量插入操作");
        
        // 获取待插入的日志列表并清空待处理列表
        lock.lock();
        try {
            if (CollectionUtils.isEmpty(pendingLogs)) {
                logger.info("[批量插入] 没有待插入的日志记录，跳过操作");
                return true;
            }
            
            // 复制所有待插入的日志
            logsToInsert.addAll(pendingLogs);
            batchSize = logsToInsert.size();
            
            // 清空待处理列表
            pendingLogs.clear();
            logger.info("[批量插入] 已获取待插入日志列表，共 {} 条记录，待处理列表已清空", batchSize);
        } finally {
            lock.unlock();
        }
        
        // 执行批量插入
        try {
            if (!CollectionUtils.isEmpty(logsToInsert)) {
                logger.info("[批量插入] 开始执行MySQL策略批量插入，记录数: {}", batchSize);
                boolean result = mysqlStrategy.batchStoreLogs(logsToInsert);
                
                // 记录插入的文件统计信息
                if (result && batchSize > 0) {
                    int successCount = 0;
                    int failureCount = 0;
                    for (FileUploadLog log : logsToInsert) {
                        if (log.getSuccessFlag() == 1) {
                            successCount++;
                        } else {
                            failureCount++;
                        }
                    }
                    logger.info("[批量插入] 本次插入统计 - 成功: {}, 失败: {}", successCount, failureCount);
                }
                
                return result;
            }
        } catch (Exception e) {
            logger.error("[批量插入] MySQL策略批量插入异常 - 错误信息: {}", e.getMessage(), e);
            
            // 失败时将日志重新添加回待处理列表
            lock.lock();
            try {
                pendingLogs.addAll(0, logsToInsert);
                logger.warn("[批量插入] 插入失败，已将 {} 条日志重新添加到待处理队列，当前待处理数量: {}", 
                        batchSize, pendingLogs.size());
            } finally {
                lock.unlock();
            }
        }
        
        logger.info("[批量插入] 操作完成，执行结果: 失败");
        return false;
    }

    /**
     * 获取当前待插入的日志数量
     * 主要用于测试场景和监控
     * @return 待插入的日志数量
     */
    public int getPendingLogCount() {
        return pendingLogs.size();
    }

    /**
     * 获取批量插入时间间隔（分钟）
     * 
     * @return 时间间隔
     */
    public int getBatchInsertInterval() {
        return batchInsertConfig.getIntervalMinutes();
    }
    
    /**
     * 获取当前配置的日志类型
     * 
     * @return 日志类型
     */
    public String getLogType() {
        return applicationConfig.getLog().getLogType();
    }

    /**
     * 获取记录数阈值
     * 
     * @return 记录数阈值
     */
    public int getRecordThreshold() {
        return batchInsertConfig.getRecordThreshold();
    }
}